package com.rongji.cms.tools.file.controller;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpException;
import cn.hutool.http.HttpUtil;
import com.rongji.cms.tools.common.annotation.CostTime;
import com.rongji.cms.tools.common.annotation.Log;
import com.rongji.cms.tools.common.core.domain.PageQuery;
import com.rongji.cms.tools.common.core.page.TableDataInfo;
import com.rongji.cms.tools.common.core.validate.QueryGroup;
import com.rongji.cms.tools.common.enums.BusinessType;
import com.rongji.cms.tools.common.exception.ServiceException;
import com.rongji.cms.tools.common.utils.file.FileUtils;
import com.rongji.cms.tools.common.utils.redis.QueueUtils;
import com.rongji.cms.tools.file.constant.FileConstant;
import com.rongji.cms.tools.file.domain.ToolsOss;
import com.rongji.cms.tools.file.domain.vo.ToolsOssVo;
import com.rongji.cms.tools.file.oss.properties.OssProperties;
import com.rongji.cms.tools.file.service.ToolsOssService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 文件相关操作
 *
 * @author yang
 * @date 2022/11/15 11:24
 * @description
 */
@RestController
@Slf4j
@RequestMapping("/file")
public class FileController {

    @Resource
    ToolsOssService toolsOssService;
    @Resource
    private OssProperties ossProperties;


    /**
     * 目的是将附件(包括原来的文档文件,转换后的pdf,图片)都存储在minio中,这样有2种方式
     * 1,先将附件上传到某个临时目录,然后进行转换,全部成功后再将这些文件再上传到minio中
     * 2,做转换主要是为了预览,那我们先保证原始文档能上传到minio,
     * 然后再异步从minio下载文档做后续转换操作,转换后的文件上传到minio操作?
     *
     * 暂定选择方式一吧,因为要做转换总要在临时目录里存储下的,如果文件大,再下载下来也很耗时间
     *
     * 上传后如果是文档，转pdf，然后pdf再转图片，这3步是并行
     * 原始文档，转换后的pdf，图片这3个上传到minio，可以同时进行，考虑使用CompletableFuture并行处理
     *
     */

    /**
     * 上传
     *
     * @param file 文件
     * @param uid  用户标识
     * @return
     */
    @CostTime
    @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @Log(title = "上传文件", businessType = BusinessType.INSERT)
    public Map<String, String> upload(@RequestPart("file") MultipartFile file, @RequestPart("uid") String uid) {
        if (ObjectUtil.isNull(file)) {
            throw new ServiceException("上传文件不能为空");
        }
        String contentType = file.getContentType();
        log.info("文件类型:{}", contentType);
        //获取文件名
        String fileName = file.getOriginalFilename();
        //获取文件后缀名 没有点符号
        String fileSuffix = FileUtils.extName(fileName);

        if (!toolsOssService.getAttachmentExtList(FileConstant.ALL_ATTACHMENT_ID).contains(fileSuffix)) {
            throw new ServiceException("不支持的文件类型!");
        }
        ToolsOss oss = toolsOssService.upload(file, uid);
//        上传成功后就给前端返回信息，转换那些操作在后台异步执行
        Map<String, String> map = new HashMap<>(2);
        map.put("url", oss.getFileUrl());
        map.put("fileName", oss.getOriginalName());
        map.put("ossId", oss.getOssId().toString());
        map.put("uid", oss.getCreateBy());
//        本地保存后，添加到相应队列，进行转换等工作
        //      用文件类型作为队列名吧
        String queueName = oss.getFileType() + FileConstant.QUEUE_SUFFIX;
        QueueUtils.addQueueObject(queueName, oss.getOssId().toString());
        log.info("通道: {} , 发送数据: {}", queueName, oss.getOssId().toString());
        return map;
    }


    /**
     * 单独的转换方法，同时如果是oss，还要上传到oss
     *
     * @param ossId
     */
    @GetMapping("/docConvert")
    public void convert(String ossId) {
        ToolsOss toolsOss = toolsOssService.getById(Long.valueOf(ossId));
        toolsOssService.docConvert(toolsOss);
        log.info("转换完成------------------------");
        if (ossProperties.isEnable()) {
            toolsOssService.upload2OssByThreads(toolsOss);
        }
        log.info("上传到oss完成------------------------");
    }

    /**
     * 下载OSS对象
     *
     * @param ossId OSS对象ID
     */
    @GetMapping("/download/{ossId}")
    public void download(@PathVariable Long ossId, HttpServletResponse response) throws IOException {
        ToolsOss toolsOss = toolsOssService.getById(ossId);
        if (ObjectUtil.isNull(toolsOss)) {
            throw new ServiceException("文件数据不存在!");
        }
        FileUtils.setAttachmentResponseHeader(response, toolsOss.getOriginalName() + "." + toolsOss.getFileSuffix());
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
        long data;
        try {

            log.info("--------------------" + toolsOss.getFileUrl());
            data = HttpUtil.download(toolsOss.getFileUrl(), response.getOutputStream(), false);
        } catch (HttpException e) {
            if (e.getMessage().contains("403")) {
                throw new ServiceException("无读取权限, 请在对应的OSS开启'公有读'权限!");
            } else {
                throw new ServiceException(e.getMessage());
            }
        }
        response.setContentLength(Convert.toInt(data));
    }


    /**
     * 删除OSS对象存储
     *
     * @param ossIds OSS对象ID串
     */
    @DeleteMapping("/{ossIds}")
    @Log(title = "删除文件", businessType = BusinessType.DELETE)
    public int remove(@NotEmpty(message = "主键不能为空")
                      @PathVariable Long[] ossIds) {
        return toolsOssService.deleteWithValidByIds(Arrays.asList(ossIds), true) ? 1 : 0;
    }



    /**
     * 查询OSS对象存储列表
     * @param bo  查询条件
     * @param pageQuery 页码
     * @return
     */
    @GetMapping("/list")
    public TableDataInfo<ToolsOssVo> list(@Validated(QueryGroup.class) ToolsOss bo, PageQuery pageQuery) {
        return toolsOssService.queryPageList(bo, pageQuery);
    }

    /**
     * 查询OSS对象基于id串
     *
     * @param ossIds OSS对象ID串
     */
    @GetMapping("/listByIds/{ossIds}")
    public List<ToolsOssVo> listByIds(@NotEmpty(message = "主键不能为空")
                                      @PathVariable Long[] ossIds) {
        List<ToolsOssVo> list = toolsOssService.listByIds(Arrays.asList(ossIds));
        return list;
    }
}
